﻿using EYangFramework.Common.AutoMapper;
using Ninject;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EYangFramework.Common.Ninject
{
    public class NinjectCore
    {
        public IKernel _kernel;
        AutoMapHelper helper;
        public NinjectCore()
        {
            _kernel = new StandardKernel();
            helper = new AutoMapHelper();
        }
        /// <summary>
        /// 初始化,用于在程序开始运行的时候注册所有的需要注册的对象
        /// 此处是一次将添加了AutoBindAssembly的程序集一次全部注册
        /// </summary>
        public void Init()
        {
            //1.获取到程序根目录下所有的dll文件
            string[] fileInfos = Directory.GetFiles(Environment.CurrentDirectory, "*.dll");
            foreach (var item in fileInfos)
            {
                Assembly asm = Assembly.LoadFile(item);
                //需要注册的程序集是添加了AutoBindAssemblyAttribute特性的程序集
                var autoBindAssembly = asm.GetCustomAttribute(typeof(AutoBindAssemblyAttribute)) as AutoBindAssemblyAttribute;
                if (autoBindAssembly == null)
                {
                    continue;
                }
                Type serviceType = autoBindAssembly.ServiceType;
                Type impleType = autoBindAssembly.ImpleType;
                Type entityType = autoBindAssembly.EntityType;
                
                Type[] currentTypes = asm.GetTypes().Where(t => t.IsPublic && !t.IsInterface && !t.IsAbstract && !t.IsGenericType).ToArray();//获取当前程序集中的所有类型
                if (currentTypes.Length <= 0)
                {
                    continue;
                }

                //在此处来注册映射关系,实现对所有类型都进行验证
                helper.AutoMapInit(asm);

                List<Type> bindTypes = new List<Type>();//用于保存当前程序集中已被绑定过得类

                //2.注册所有的指定对象
                if (entityType != null && (serviceType == null || impleType == null))
                {
                    throw new NinjectException(asm.FullName + "程序集AutoBindAssemblyAttribute特性指定了实体基类,但未指定服务类型或服务基类!");
                }
                else if (entityType == null && (serviceType != null || impleType != null))
                {
                    throw new NinjectException(asm.FullName + "程序集AutoBindAssemblyAttribute特性指定了服务类型或服务基类,但未指定对应实体基类!");
                }
                else if (entityType != null)
                {
                    //2.1绑定所有的泛型类
                    bindTypes = RegisterGenericType(serviceType, impleType, entityType, currentTypes);
                }

                //2.2注册所有其他类型
                //AutoBindAssemblyAttribute中定义的注册方法优先级高于添加了AutoBindAttribute特性的类型
                //移除掉注册过得类,防止被重复注册
                RegisterCommonType(currentTypes.Except(bindTypes).ToArray());
            }
        }

        public List<Type> RegisterGenericType(Type service, Type imple,Type entity, Type[] types)
        {
            List<Type> bindTypes = new List<Type>();
            foreach (var current in types)
            {
                //只有继承自实例基类或接口的类,才会被注册
                if ((current.IsSubclassOf(entity)&& !IgnoreCurrentType(current)) ||(entity.IsAssignableFrom(current) && !IgnoreCurrentType(current)))
                {
                    Type serviceType = service.MakeGenericType(current);
                    Type implType = imple.MakeGenericType(current);
                    _kernel.Bind(serviceType).To(implType);
                    bindTypes.Add(current);
                }
            }
            return bindTypes;
        }

        public void RegisterCommonType(Type[] types)
        {
            //1.注册窗体类
            //窗体类优先级高于添加了AutoBindAttribute特性的类
            //2.注册添加了AutoBindAttribute特性的类
            foreach (var type in types)
            {
                if (type.IsSubclassOf(typeof(Form))&& !type.GetCustomAttributes().Any(t => t is IgnoreAttribute))
                {
                    //此处可注册,因为Get方法本身就会返回一个实例化的对象
                    this.AddRegistraterToSelf(type, false);
                    continue;
                }
                if (ShouldRegistrater(type))
                {
                    //如果自定义了绑定的规则,则使用自定义的规则
                    AutoBindAttribute autoBind = type.GetCustomAttribute(typeof(AutoBindAttribute)) as AutoBindAttribute;
                    if (autoBind == null)
                    {
                        autoBind = type.GetInterfaces().Where(t => t.GetCustomAttributes(typeof(AutoBindAttribute)).Count() > 0).FirstOrDefault().GetCustomAttribute(typeof(AutoBindAttribute)) as AutoBindAttribute;
                    }
                    bool singleton = ShouldRegistraterBySingleton(type);
                    if (autoBind.ServiceType != null && autoBind.ImpleType != null)
                    {
                        this.AddRegistrater(autoBind.ServiceType, autoBind.ImpleType, autoBind.IsSington);
                        continue;
                    }
                    else if (autoBind.ServiceType == null && autoBind.ImpleType != null)
                    {
                        this.AddRegistraterToSelf(autoBind.ImpleType, autoBind.IsSington);
                        continue;
                    }
                    //根据实现的接口来绑定,此模式下,因为不是泛型绑定,所以要求一个实现只能对应唯一的一个接口
                    Type[] interfaces = type.GetInterfaces();
                    if (interfaces.Length <= 0)
                    {
                        this.AddRegistraterToSelf(type, autoBind.IsSington);
                    }
                    else if (interfaces.Length == 1)
                    {
                        this.AddRegistrater(interfaces[0], type, autoBind.IsSington);
                    }
                    else
                    {
                        Type[] services = interfaces.Where(t => t.Name == "I" + type.Name).ToArray();
                        if (services.Count() == 1)
                        {
                            this.AddRegistrater(services[0], type, autoBind.IsSington);
                        }
                        else
                        {
                            this.AddRegistraterToSelf(type, autoBind.IsSington);
                        }
                    }
                }
            }
        }

        public bool IgnoreCurrentType(Type type)
        {
            return type.GetCustomAttributes().Any(t => t is IgnoreAttribute);
        }

        public T GetService<T>()
        {
            return _kernel.TryGet<T>();
        }
        public object GetService(Type type)
        {
            return _kernel.TryGet(type);
        }

        public bool ShouldRegistraterBySingleton(Type currentType)
        {
            bool singleton = false;
            if (!currentType.IsAbstract && currentType.IsPublic)
            {
                foreach (var item in currentType.GetCustomAttributes())
                {
                    if (item is AutoBindAttribute)
                    {
                        singleton = (item as AutoBindAttribute).IsSington;
                        break;
                    }
                }
            }
            return singleton;
        }
        /// <summary>
        /// 此方法判断指定了AutoBindAttribute特性的对象,对于其他的对象,仓储类要求必须被注册,所有的窗体要求必须被注册
        /// Ignore优先级高于AutoBind
        /// </summary>
        /// <param name="currentType"></param>
        /// <returns></returns>
        private bool ShouldRegistrater(Type currentType)
        {
            if (!currentType.IsAbstract && currentType.IsPublic)
            {
                var igAttr = currentType.GetCustomAttributes(typeof(IgnoreAttribute));
                if (igAttr != null && igAttr.Count() > 0)
                {
                    return false;
                }
                var abAttr = currentType.GetCustomAttributes(typeof(AutoBindAttribute), true);
                if (abAttr == null || abAttr.Count() <= 0)
                {
                    var currentTypeInterfaces = currentType.GetInterfaces();
                    return currentTypeInterfaces.Any(t => t.GetCustomAttributes(typeof(AutoBindAttribute), true).Count() > 0);
                }
                return true;
            }
            return false;
        }

        public void AddRegistrater<TInterface, TImple>(bool isSington) where TImple : TInterface
        {
            if (!isSington)
            {
                _kernel.Bind<TInterface>().To<TImple>();
            }
            else
            {
                _kernel.Bind<TInterface>().To<TImple>().InSingletonScope();
            }
        }
        public void AddRegistrater(Type serviceType, Type implType, bool isSington)
        {
            if (!isSington)
            {
                _kernel.Bind(serviceType).To(implType);
            }
            else
            {
                _kernel.Bind(serviceType).To(implType).InSingletonScope();
            }
        }

        public void AddRegistraterToSelf<TSelf>(bool isSington)
        {
            if (!isSington)
            {
                _kernel.Bind<TSelf>().ToSelf();
            }
            else
            {
                _kernel.Bind<TSelf>().ToSelf().InSingletonScope();
            }
        }
        public void AddRegistraterToSelf(Type implType, bool isSington)
        {
            if (!isSington)
            {
                _kernel.Bind(implType).ToSelf();
            }
            else
            {
                _kernel.Bind(implType).ToSelf().InSingletonScope();
            }
        }
    }
}
